package com.ym.medical.service.impl;

import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.ym.common.constant.Constants;
import com.ym.common.constant.DictEnum;
import com.ym.common.enums.LoginMethodEnum;
import com.ym.common.enums.SmsTemplateEnum;
import com.ym.common.enums.UserUserTypeEnum;
import com.ym.common.utils.CalcUtil;
import com.ym.common.utils.SmsUtil;
import com.ym.core.annotation.Locker;
import com.ym.core.config.WxProperties;
import com.ym.core.exception.BusinessException;
import com.ym.core.rabbit.IMessageSenderSV;
import com.ym.core.redis.RedisKeyEnum;
import com.ym.core.shiro.ShiroUtil;
import com.ym.core.utils.tkmybatis.TK;
import com.ym.medical.dao.InquiryOrderDao;
import com.ym.medical.domain.bo.pay.WxApplyPaySign;
import com.ym.medical.domain.bo.pay.WxOrderQueryRespData;
import com.ym.medical.domain.bo.pay.WxRefundQueryRespData;
import com.ym.medical.domain.entity.*;
import com.ym.medical.domain.param.InquiryOrderParam;
import com.ym.medical.domain.param.MindParam;
import com.ym.medical.domain.param.OrderParam;
import com.ym.medical.domain.vo.MindListMobileVO;
import com.ym.medical.enums.*;
import com.ym.medical.service.*;
import com.ym.medical.service.task.AutoRefundOrderTask;
import me.chanjar.weixin.mp.api.WxMpService;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.BuilderException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestParam;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 订单支付相关
 *
 * @author: 李涛
 * @version: 2019年07月11日 16:16
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class OrderPaySVImpl implements IOrderPaySV {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderPaySVImpl.class);

    @Autowired
    private IInquiryOrderSV inquiryOrderSV;

    @Autowired
    private WxProperties wxProperties;

    @Autowired
    private IOrderFlowRecordSV orderFlowRecordSV;

    @Autowired
    private ICounsellingRoomSV counsellingRoomSV;

    @Autowired
    private IMindSV mindSV;

    @Autowired
    private IPersonalTransactionOrderSV personalTransactionOrderSV;

    @Autowired
    private IDoctorStudioSV doctorStudioSV;

    @Autowired
    private IUserSV userSV;

    @Autowired
    private IAccountSV accountSV;

    @Autowired
    private IConsumeDetailsSV consumeDetailsSV;

    @Autowired
    private IOrderPoolSV orderPoolSV;

    @Autowired
    private InquiryOrderDao inquiryOrderDao;

    @Autowired
    private IJpushAccountSV jpushAccountSV;

    @Autowired
    private IInformationSV informationSV;

    @Autowired
    private ITransactionDetailsSV transactionDetailsSV;

    @Autowired
    private ISysCodeRuleSV generateCodeRuleSV;

    @Autowired
    private IYunxinUserSV iYunxinUserSV;

    @Autowired
    private IStudioPersonnelSV iStudioPersonnelSV;

    @Autowired
    private ICounsellingRoomSV iCounsellingRoomSV;

    @Autowired
    private IChatroomPersonnelSV chatroomPersonnelSV;

    @Autowired
    private IPlatformTradeDetailSV platformTradeDetailSV;

    @Autowired
    private ICouponReceiveRecordSV couponReceiveRecordSV;

    @Autowired
    private IPhoneRuleSV phoneRuleSV;

    @Autowired
    private IPatientDetailSV patientDetailSV;

    @Autowired
    private ISysSignSV sysSignSV;

    @Autowired
    private ISysConfigSV sysConfigSV;

    @Resource
    private WxMaServiceImpl wxMaService;

    @Autowired
    private IMessageSenderSV messageSenderSV;

    @Autowired
    private IPlatformTradeBillSV platformTradeBillSV;

    @Autowired
    private IDoctorServiceItemSV doctorServiceItemSV;

    @Autowired
    private ICouponSV couponSV;

    @Autowired
    private IWxPaySV wxPaySV;

    @Autowired
    @Lazy
    private WxMpService wxMpService;

    private String payUrl = null;

    //退费url
    private String refundUrl = null;

    //提现url
    private String withdrawUrl = null;


    /**
     * 问诊订单退费申请
     *
     * @param orderModel 必传参数 productId orderModel
     * @return
     */
    @Override
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "0#productId", noGetMsg = "该订单医生已答复！")
    public JSONObject refundOrder(OrderParam orderModel) {
        //1. 判断该问诊订单是否可退费
        InquiryOrderEntity order = checkRefundOrder(orderModel);

        //2. 进入退款环节
        PaymentWayEnum enumByCode = DictEnum.valueOfEnum(PaymentWayEnum.class, order.getPaymentWay());
        switch (enumByCode) {
            case WECHAT:
                //修改订单状态为退费申请中
                order.setPaymentState(InquiryOrderPaymentStateEnum.REFUND_APPLYING.getKey());
                inquiryOrderSV.updateById(order);

                //微信退款
                String callbackUrl = StringUtils.isBlank(orderModel.getCallbackUrl()) ? "/patient/order/orderRefundCallback" : orderModel.getCallbackUrl();
                return wxPaySV.refundApply(order.getIoNum(), WxTradeTypeEnum.JSAPI, null, order.getPaymentPrice(), callbackUrl);
            case ACCOUNT:
                JSONObject result = new JSONObject();
                try {
                    //余额退费
                    order.setPaymentTime(new Date());
                    wxrefundUpdateOrderInfo(order, generateCodeRuleSV.createCode(CodeRuleEnum.TRANSACTION_SERIAL_NUMBER));
                    result.put("returnCode", "SUCCESS");
                    result.put("resultCode", "SUCCESS");
                    result.put("mes", "退费成功");
                } catch (Exception e) {
                    LOGGER.error(ExceptionUtil.getMessage(e));
                    result.put("returnCode", "FAIL");
                    result.put("resultCode", "FAIL");
                    result.put("mes", "退费失败");
                }
                return result;
            default:
                break;
        }
        return null;
    }

    /**
     * 检查退费订单
     *
     * @param orderModel
     * @return
     */
    private InquiryOrderEntity checkRefundOrder(OrderParam orderModel) {
        InquiryOrderEntity order = getInquiryOrder(orderModel.getOutTradeNo());
        if (order == null || InquiryOrderPaymentStateEnum.REFUND_OVER.getKey().equals(order.getPaymentState())) {
            throw new BusinessException("该订单不存在或该订单已经退过费了");
        }

        if (order.getPaymentPrice() == null || order.getPaymentPrice().doubleValue() <= 0) {
            throw new BuilderException("订单金额必须大于0");
        }

        //2. 查询聊天室回复状态
        List<CounsellingRoomEntity> counsellingRooms = counsellingRoomSV.selectList(
                TK.select(CounsellingRoomEntity.class).where()
                        .andEqualTo(CounsellingRoomEntity::getIoId, order.getId())
                        .end()
                        .orderBy(CounsellingRoomEntity::getId)
                        .desc()
        );
        for (CounsellingRoomEntity counsellingRoom : counsellingRooms) {
            if (!CounsellingRoomStateEnum.DID_NOT_RETURN.getKey().equals(counsellingRoom.getCourState())) {
                throw new BusinessException("当前订单已经被答复,不能退款");
            }
        }
        return order;
    }

    public InquiryOrderEntity getInquiryOrder(@RequestParam String ioNum) {
        List<InquiryOrderEntity> inquiryOrders = inquiryOrderSV.selectList(InquiryOrderEntity.builder().ioNum(ioNum).build());
        InquiryOrderEntity inquiryOrder = (inquiryOrders.size() > 0) ? inquiryOrders.get(0) : null;
        return inquiryOrder;
    }

    /**
     * 微信支付请求申请
     *
     * @param orderModel
     * @return
     */
    @Override
    public WxApplyPaySign.SignData payApply(OrderParam orderModel) {
        try {
            //1. 判断支付类型
            String payType = orderModel.getPayType();
            if (StrUtil.isBlank(payType)) {
                throw new BusinessException("请传入payType (01-订单支付 02-送心意 03-充值)");
            }

            String serviceUrl = null;    //业务系统回调地址
            String body = null;        //商品描述
            BigDecimal paymentPrice = null;        //订单金额
            String outTradeNo = orderModel.getOutTradeNo();
            String tradeType = orderModel.getTradeType();
            String paySource = null;
            if ("JSAPI".equals(tradeType)) {
                paySource = PlaformSourceEnum.SMALL_PROGRAM.getKey();
            } else if ("MWEB".equals(tradeType)) {
                paySource = PlaformSourceEnum.THE_PUBLIC.getKey();
            }
            switch (payType) {
                case Constants.PayWay.ORDER_PAY:
                    serviceUrl = wxProperties.getPay().getCallBackIp() + "/patient/order/orderPayCallBack";
                    InquiryOrderEntity order = checkInquiryOrder(outTradeNo);
                    //更新问诊订单
                    order.setPaymentTime(new Date());
                    order.setPayAcc(orderModel.getOpenId());
                    order.setPaymentWay(PaymentWayEnum.WECHAT.getKey());
                    order.setPaySource(paySource);
                    inquiryOrderSV.updateById(order);
                    paymentPrice = order.getPaymentPrice();
                    // todo 此处取的ioTypeName
                    body = order.getIoType();
                    break;
                case Constants.PayWay.GIVE_MIND:
                    serviceUrl = wxProperties.getPay().getCallBackIp() + "/patient/order/mindOrderPayCallBack";
                    MindListMobileVO mind = checkMindOrder(outTradeNo);

                    //更新送心意订单
                    MindEntity mindEntity = new MindEntity();
                    BeanUtils.copyProperties(mind, mindEntity);
                    mindEntity.setPayAcc(orderModel.getOpenId());
                    mindEntity.setPayType(PaymentWayEnum.WECHAT.getKey());
                    mindEntity.setTdTime(new Date());
                    mindEntity.setPaySource(paySource);
                    mindSV.updateById(mindEntity);

                    paymentPrice = mind.getThankMoney();
                    body = "送心意";
                    break;
                case Constants.PayWay.RECHARGE:
                    serviceUrl = wxProperties.getPay().getCallBackIp() + "/patient/order/rechargeCallBack";
                    PersonalTransactionOrderEntity personalTransactionOrder = checkPersonalTransactionOrder(outTradeNo);

                    //更新充值订单
                    personalTransactionOrder.setTdTime(new Date());
                    personalTransactionOrder.setDfAcc(orderModel.getOpenId());
                    personalTransactionOrder.setDfAccType(PaymentWayEnum.WECHAT.getKey());
                    personalTransactionOrder.setPaySource(paySource);
                    personalTransactionOrderSV.updateById(personalTransactionOrder);

                    paymentPrice = personalTransactionOrder.getTdAmount();
                    body = "充值";
                    break;
                default:
                    break;
            }
            //发起交易
            String openId = StrUtil.isBlank(orderModel.getOpenId())
                    ? ShiroUtil.getLoginUser(UserPatientViewEntity.class).getOpenidMini() : orderModel.getOpenId();
            body = StrUtil.isBlank(orderModel.getBody())
                    ? body : orderModel.getBody();
            WxApplyPaySign.SignData signData = wxPaySV.payApply(openId, outTradeNo, WxTradeTypeEnum.JSAPI, body, paymentPrice, serviceUrl);
            return signData;
        } catch (Exception e) {
            LOGGER.info(e.getMessage());
            throw new BusinessException("微信支付失败");
        }
    }


    /**
     * 检查充值提现订单
     *
     * @param outTradeNo
     * @return
     */
    private PersonalTransactionOrderEntity checkPersonalTransactionOrder(String outTradeNo) {
        PersonalTransactionOrderEntity personalTransactionOrder = personalTransactionOrderSV.selectByIoNum(outTradeNo);
        if (personalTransactionOrder == null || PaymentStateEnum.HAVE_TO_PAY.getKey().equals(personalTransactionOrder.getPayState())) {
            throw new BusinessException("该充值/提现订单不存在或已支付");
        }
        if (personalTransactionOrder.getTdAmount() == null || personalTransactionOrder.getTdAmount().doubleValue() <= 0) {
            throw new BusinessException("订单金额必须大于0");
        }
        return personalTransactionOrder;
    }

    /**
     * 检查送心意订单
     *
     * @param ioNum
     * @return
     */
    private MindListMobileVO checkMindOrder(String ioNum) {
        MindListMobileVO mindModel = mindSV.getMindModelByIoNum(ioNum);
        if (mindModel == null) {
            throw new BusinessException("送心意订单不能为空");
        }
        if (mindModel.getThankMoney() == null || mindModel.getThankMoney().compareTo(BigDecimal.ZERO) <= 0) {
            throw new BusinessException("订单金额必须大于0");
        }
        return mindModel;
    }

    /**
     * 获取交易签名头信息
     *
     * @param reqBody
     * @return
     */
    public Map<String, String> getTdSignHeaders(String reqBody) {
        Map<String, String> headers = new HashMap<>();
        long now = System.currentTimeMillis();
        headers.put("appKey", Constants.SysConfigKey.YM_PAY);
        headers.put("timestamp", String.valueOf(now));
        //获取支付平台秘钥
        SysSignEntity sysSign = sysSignSV.findSysSignByAppKey(Constants.SysConfigKey.YM_PAY);
        String sign = SecureUtil.md5(reqBody + "" + sysSign.getAppSecret() + "ym" + now);
        headers.put("sign", sign);
        return headers;
    }

    /**
     * 检查订单是否可支付
     *
     * @param ioNum
     * @return
     */
    private InquiryOrderEntity checkInquiryOrder(String ioNum) {
        InquiryOrderEntity order = getInquiryOrder(ioNum);
        if (order == null) {
            throw new BusinessException("该订单不存在");
        }
        if (!ShiroUtil.getLoginId(true).equals(order.getUserId())) {
            throw new BusinessException("非法操作");
        }
        OrderFlowRecordEntity orderFlowRecord = orderFlowRecordSV.findNewByOrderId(order.getId());
        if (orderFlowRecord.getDoctorId() == null && orderFlowRecord.getDoctorDsId() == null) {
            throw new BusinessException("订单流转记录中抢单医生和工作室不能同时为空");
        }
        if (order.getPaymentPrice() == null || order.getPaymentPrice().doubleValue() <= 0) {
            throw new BusinessException("订单金额必须大于0");
        }
        AccountEntity patientAccount = accountSV.selectAccountByUserId(order.getUserId());
        if (order.getPaymentPrice().doubleValue() > patientAccount.getAccBalance().doubleValue()) {
            throw new BusinessException("该患者的账户余额不足");
        }
        return order;
    }

    /**
     * 微信问诊定时任务补调
     *
     * @param order
     */
    @Override
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "0#id", noGetMsg = "请稍后重试")
    public void wxPayOrder(InquiryOrderEntity order) throws Exception {
        //1. 查询微信支付订单
        WxOrderQueryRespData wxNotifyRespData = orderPayQuery(order.getIoNum(), ConsumeDetailsTdTypeEnum.valueOf(order.getIoType()));

        //2. 执行交易状态所对应的业务
        WxTdStateEnum wxTdStateEnum = WxTdStateEnum.valueOf(wxNotifyRespData.getTradeState());
        switch (wxTdStateEnum) {
            case SUCCESS: //支付成功
                String timeEnd = wxNotifyRespData.getTimeEnd();
                order.setPaymentTime(DateUtil.parseDate(timeEnd));
                updateOrderInfo(order, wxNotifyRespData.getTransactionId());
                break;
            case REFUND: //转入退款
                wxrefundOrder(order);
                break;
            case NOTPAY: //未支付
                order.setPaymentState(InquiryOrderPaymentStateEnum.TO_BE_PAID.getKey());
                inquiryOrderSV.updateById(order);
                break;
            default:
                order.setPaymentState(InquiryOrderPaymentStateEnum.ERROR.getKey());
                inquiryOrderSV.updateById(order);
                break;
        }


    }

    /**
     * 微信退费回调
     *
     * @param order
     */
    @Override
    public void wxrefundOrder(InquiryOrderEntity order) {
        WxRefundQueryRespData wxRefundQueryRespData = refundOrderQuery(order.getIoNum());

        List<String> refundStatusList = wxRefundQueryRespData.getRefundStatusList();    //退费状态
        List<String> refundTimeList = wxRefundQueryRespData.getRefundTimeList();    //退费时间
        String transactionId = wxRefundQueryRespData.getTransactionId();            //退费流程号

        String refundStatus = refundStatusList.get(0);
        Date refundTime = StringUtils.isBlank(refundTimeList.get(0)) ? null : DateUtil.parseDate(refundTimeList.get(0));
        if (!refundStatusList.isEmpty()) {
            if (WxRefundStateEnum.PROCESSING.getKey().equals(refundStatus)) {
                return;
            }
            if (!WxRefundStateEnum.SUCCESS.getKey().equals(refundStatus)) {
                //如果退款异常 订单支付状态置为已支付
                order.setPaymentState(InquiryOrderPaymentStateEnum.HAVE_TO_PAY.getKey());
                inquiryOrderSV.updateById(order);
            } else {
                order.setPaymentTime(refundTime);
                wxrefundUpdateOrderInfo(order, transactionId);
            }
        }
    }

    /**
     * 申诉订单退费回调
     *
     * @param order
     */
    @Override
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "['$.ioNum']", noGetMsg = "请稍候！")
    public void complainOrderRefundCallback(InquiryOrderEntity order) {
        WxRefundQueryRespData wxRefundQueryRespData = refundOrderQuery(order.getIoNum());

        List<String> refundStatusList = wxRefundQueryRespData.getRefundStatusList();    //退费状态
        List<String> refundTimeList = wxRefundQueryRespData.getRefundTimeList();    //退费时间
        String transactionId = wxRefundQueryRespData.getTransactionId();            //退费流程号

        String refundStatus = refundStatusList.get(0);
        Date refundTime = StringUtils.isBlank(refundTimeList.get(0)) ? null : DateUtil.parseDate(refundTimeList.get(0));
        if (!refundStatusList.isEmpty()) {
            if (WxRefundStateEnum.PROCESSING.getKey().equals(refundStatus)) {
                return;
            }
            if (!WxRefundStateEnum.SUCCESS.getKey().equals(refundStatus)) {
                //如果退款异常 订单支付状态置为已支付
                order.setPaymentState(InquiryOrderPaymentStateEnum.HAVE_TO_PAY.getKey());
                inquiryOrderSV.updateById(order);
            } else {
                order.setPaymentTime(refundTime);
                complainOrderRefund(order, transactionId);
            }
        }

    }

    @Override
    public void complainOrderRefund(InquiryOrderEntity order, String otherBillTdNo) {
        //1.判断订单
        if (order == null || InquiryOrderPaymentStateEnum.REFUND_OVER.getKey().equals(order.getPaymentState())) {
            LOGGER.error("该订单不存在或该订单已经退过费了");
            return;
        }
        //-------------------------------------------变量 start------------------------------------------------------------------------------------
        //订单信息
        Long patientId = order.getUserId();
        UserEntity user = userSV.selectById(patientId);            //用户信息
        String userName = user.getRealName() == null ? user.getNickName() : user.getRealName();
        String ioNum = order.getIoNum();        //订单号
        Date tdTime = order.getPaymentTime();    //交易时间
        // todo 此处取ioTypeName
        String ioTypeName = order.getIoType();        //订单号
        String payAcc = order.getPayAcc();                //账户
        BigDecimal tdAmount = order.getPaymentPrice();
        String paymentWay = order.getPaymentWay();
        Long couponRecordId = order.getRelId();   //优惠劵领取记录主键

        OrderFlowRecordEntity orderFlowRecord = orderFlowRecordSV.findNewByOrderId(order.getId());        //查询订单中医生最新的订单流转记录
        Long doctorId = orderFlowRecord.getDoctorId();                //医生主键
        Long doctorDsId = orderFlowRecord.getDoctorDsId();            //获取医生的工作室id
        //获取抢单人
        Long payee = (doctorDsId == null) ? doctorId : doctorStudioSV.selectById(doctorDsId).getBelongPerson();  //有工作室取宿主 没有工作室取抢单人
        AccountEntity doctorAcc = accountSV.selectAccountByUserId(payee);                //获取抢单医生的基本信息
        UserEntity payeeUser = userSV.selectById(payee);
        String payeeName = payeeUser.getRealName();


        BigDecimal paymentPrice = BigDecimal.valueOf(0);  //实际支付金额
        BigDecimal discountAmount = BigDecimal.valueOf(0);    //平台垫付金额
        BigDecimal channelAmt = BigDecimal.valueOf(0);  //渠道手续费

        //使用优惠劵时获取平台垫付金额 渠道方手续费
        if (couponRecordId != null) {
            CouponEntity coupon = couponSV.selectByReciveId(couponRecordId);
            if (coupon != null && CouponChannelEnum.PLATFORM.getKey().equals(coupon.getIsSys())) {
                InquiryOrderParam orderModel = new InquiryOrderParam();
                orderModel.setRelId(order.getRelId());
                orderModel.setPriceId(order.getRuleId());
                Map<String, BigDecimal> payAmt = doctorServiceItemSV.couponUse(orderModel);
                paymentPrice = payAmt.get("paymentPrice");   //实际支付金额
                discountAmount = payAmt.get("discountAmount");    //平台垫付金额
                if (!PaymentWayEnum.ACCOUNT.equals(paymentWay)) {
                    channelAmt = CalcUtil.mul(paymentPrice, 0.006).decimalValue(2);    //渠道手续费
                }
            }
        }

        ConsumeDetailsTdTypeEnum ioTypeEnum = DictEnum.valueOfEnum(ConsumeDetailsTdTypeEnum.class, order.getIoType());
        PaymentWayEnum paymentWayEnum = DictEnum.valueOfEnum(PaymentWayEnum.class, paymentWay);

        //-------------------------------------------变量 end------------------------------------------------------------------------------------

        //2.退款成功 修改订单状态 订单流转记录状态 （已结单）
        orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.THE_CANCELLATION.getKey());
        orderFlowRecordSV.updateById(orderFlowRecord);
        order.setIoState(InquiryOrderStateEnum.CANCEL.getKey());
        order.setPaymentState(InquiryOrderPaymentStateEnum.REFUND_OVER.getKey());
        //如果使用优惠劵 更改优惠劵为有效状态
        if (couponRecordId != null) {
            CouponReceiveRecordEntity couponReceiveRecord = couponReceiveRecordSV.selectById(couponRecordId);
            couponReceiveRecord.setUserState(CouponUseStateEnum.DONT_USE.getKey());
            couponReceiveRecordSV.updateById(couponReceiveRecord);
        }
        inquiryOrderDao.updateByPrimaryKeySelective(order);

        //3. 更新患者的冻结余额
        AccountEntity account = updatePatientFreeze(patientId, tdAmount, "subtract", paymentWay);

        //4. 新增患者的消费明细
        generateConsumeDetails(account.getId(), CashFlowEnum.INCOME, ioNum, otherBillTdNo, payeeName + "医生的" + ioTypeName + "费-退款", ioTypeEnum,
                paymentWayEnum, payAcc, tdAmount, discountAmount, channelAmt, account.getAccBalance(), tdTime, doctorDsId, doctorAcc.getId());

        //4. 构建退费消息并发送
        informationSV.generateRefordInformation(order, payee);
        if (doctorDsId != null) {
            orderPoolSV.remove(RedisKeyEnum.SPECIALIST_DOCTOR_POOL, order.getId().toString(), "WORK", doctorDsId.toString());
        }

        //5. 新增平台交易账单
        if (!PaymentWayEnum.ACCOUNT.getKey().equals(paymentWay)) {
            //用户姓名,用户手机号,产品名称,交易明细内容,商户订单号,支付流水号,业务类型(01:支付;02:退费;),付款账户,交易金额,交易时间,支付方式,对账状态(01:未对账;02:已对账),创建人,创建时间，修改人,修改时间
            // todo 此处取ioTypeName
            platformTradeBillSV.insertPlatformTradeBill(userName, user.getPhoneNo(), order.getIoType(), "问诊订单申诉退费", order.getIoNum(),
                    BillBusTypeEnum.REFUND, order.getPayAcc(), tdAmount, order.getPaymentTime(), DictEnum.valueOfEnum(PaymentWayEnum.class, order.getPaymentWay()), BillStatusEnum.DID_NOT_CHECK, null, otherBillTdNo);
        }

    }

    /**
     * 微信送心意回调接口
     *
     * @param ioNum
     */
    @Override
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "0", noGetMsg = "请稍候！")
    public void mindOrder(String ioNum) {
        MindListMobileVO mindModel = mindSV.getMindModelByIoNum(ioNum);
        WxOrderQueryRespData respData = orderPayQuery(ioNum, ConsumeDetailsTdTypeEnum.TO_MIND);
        mindModel.setTdTime(DateUtil.parseDate(respData.getTimeEnd()));
        updateMindOrder(mindModel, respData.getTransactionId());

    }

    /**
     * 提现交易回调
     *
     * @param ioNum
     */
    @Override
    public void withdrawCallBack(String ioNum) {
        PersonalTransactionOrderEntity personalTransactionOrder = personalTransactionOrderSV.selectByIoNum(ioNum);
        WxOrderQueryRespData respData = orderPayQuery(ioNum, ConsumeDetailsTdTypeEnum.WITHDRAW);
        personalTransactionOrder.setTdTime(DateUtil.parseDate(respData.getTimeEnd()));
        updateRechargeOrder(personalTransactionOrder, respData.getTransactionId());
    }

    /**
     * 微信充值回调
     *
     * @param orderModel
     */
    @Override
    public void rechargeCallBack(OrderParam orderModel) {
        String ioNum = orderModel.getOutTradeNo();
        PersonalTransactionOrderEntity personalTransactionOrder = personalTransactionOrderSV.selectByIoNum(ioNum);
        WxOrderQueryRespData respData = orderPayQuery(ioNum, ConsumeDetailsTdTypeEnum.RECHARGE);
        //获取交易单号
        String timeEnd = respData.getTimeEnd();
        personalTransactionOrder.setTdTime(DateUtil.parseDate(timeEnd));
        updateRechargeOrder(personalTransactionOrder, respData.getTransactionId());

    }


    //------------------------------------------------------------ start 账户余额支付api ------------------------------------------------------------------------

    /**
     * 账户余额支付
     *
     * @param orderModel
     * @throws Exception
     */
    @Override
    public void accountPay(OrderParam orderModel) throws Exception {
        //1. 支付密码验证
        Long loginId = ShiroUtil.getLoginId(true);
        UserEntity userEntity = userSV.selectById(loginId);
        if (StringUtils.isNotBlank(orderModel.getPayPwd())) {
            String newPwd = ShiroUtil.genHashPass(orderModel.getPayPwd(), userEntity.getSalt());
            if (!newPwd.equals(userEntity.getPayPwd())) {
                throw new BusinessException("支付密码不正确！");
            }
        }

        //2. 获取支付来源
        LoginMethodEnum loginMethod = ShiroUtil.getLoginUser(true).getLoginMethod();
        String paySource = loginMethod.getKey();


        //3. 判断支付类型
        String ioNum = orderModel.getOutTradeNo();
        String payType = orderModel.getPayType();
        if (StrUtil.isBlank(payType)) {
            throw new BusinessException("请传入payType (01-订单支付 02-送心意 03-充值)");
        }

        switch (payType) {
            case Constants.PayWay.ORDER_PAY:
                InquiryOrderEntity order = checkInquiryOrder(ioNum);
                //更改订单信息
                order.setPaymentWay(PaymentWayEnum.ACCOUNT.getKey());
                order.setPaymentTime(new Date());
                order.setPaySource(paySource);
                updateOrderInfo(order, generateCodeRuleSV.createCode(CodeRuleEnum.TRANSACTION_SERIAL_NUMBER));
                break;
            case Constants.PayWay.GIVE_MIND:
                MindListMobileVO mindModel = checkMindOrder(ioNum);

                //更改订单信息
                mindModel.setPayType(PaymentWayEnum.ACCOUNT.getKey());
                mindModel.setTdTime(new Date());
                mindModel.setPaySource(paySource);
                updateMindOrder(mindModel, generateCodeRuleSV.createCode(CodeRuleEnum.TRANSACTION_SERIAL_NUMBER));
                break;
        }

    }
    //------------------------------------------------------------ end 账户余额支付api ------------------------------------------------------------------------


    //------------------------------------------------------------ start 微信支付api ------------------------------------------------------------------------

    /**
     * 交易订单查询
     *
     * @param ioNum
     * @return
     */
    @Override
    public WxOrderQueryRespData orderPayQuery(String ioNum, ConsumeDetailsTdTypeEnum tdTypeEnum) {
        //1. 获取支付来源 小程序/公众号
        String paySource = getPaySource(ioNum, tdTypeEnum);

        //2. 调用微信查询
        WxOrderQueryRespData wxNotifyRespData = wxPaySV.payOrderQuery(PlaformSourceEnum.valueOf(paySource), ioNum);
        return wxNotifyRespData;
    }

    /**
     * 获取支付来源
     *
     * @param ioNum
     * @param tdTypeEnum
     * @return
     */
    private String getPaySource(String ioNum, ConsumeDetailsTdTypeEnum tdTypeEnum) {
        String paySource = null;
        switch (tdTypeEnum) {
            case BY_CONSULTING:
            case TELEPHONE_COUNSELING:
            case VIDEO_CONSULTATION:
                paySource = getInquiryOrder(ioNum).getPaySource();
                break;
            case RECHARGE:
            case WITHDRAW:
                paySource = personalTransactionOrderSV.selectByIoNum(ioNum).getPaySource();
                break;
            case TO_MIND:
                paySource = mindSV.getMindModelByIoNum(ioNum).getPaySource();
                break;
        }
        return paySource;
    }


    /**
     * 微信退款订单查询
     *
     * @param ioNum
     * @return
     */
    @Override
    public WxRefundQueryRespData refundOrderQuery(String ioNum) {
        InquiryOrderEntity inquiryOrder = getInquiryOrder(ioNum);
        WxRefundQueryRespData wxRefundQueryRespData = wxPaySV
                .refundOrderQuery(PlaformSourceEnum.valueOf(inquiryOrder.getPaySource()), ioNum);
        return wxRefundQueryRespData;
    }


    /**
     * 微信提现申请
     *
     * @param outTradeNo
     */
    @Override
    public void withdraw(String outTradeNo) {
        //1. 获取openid
        String openId = ShiroUtil.getLoginUser(UserPatientViewEntity.class).getOpenidMini();

        //2. 获取提现订单
        PersonalTransactionOrderEntity withdrawOrder = checkPersonalTransactionOrder(outTradeNo);

        //3. 发起提现
        wxPaySV.withdrawApply(openId, outTradeNo, WxTradeTypeEnum.JSAPI, withdrawOrder.getTdAmount());
    }

    //------------------------------------------------------------ end 微信支付api-------------------------------------------------------------------------

    //------------------------------------------------------------ start 订单修改状态相关-------------------------------------------------------------------------

    /**
     * 充值提现修改订单信息
     *
     * @param personalTransactionOrder
     */
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "['$.ioNum']", noGetMsg = "请稍候！")
    @Override
    public void updateRechargeOrder(PersonalTransactionOrderEntity personalTransactionOrder, String otherBillTdNo) {
        //1. 检查订单
        if (personalTransactionOrder == null) {
            throw new BusinessException("该充值订单不存在");
        } else if (PaymentStateEnum.HAVE_TO_PAY.getKey().equals(personalTransactionOrder.getPayState())) {
            throw new BusinessException("该充值订单已经支付过了");
        }
        //2.修改订单状态
        personalTransactionOrder.setPayState(PaymentStateEnum.HAVE_TO_PAY.getKey());
        personalTransactionOrder.setTdState(TransactionStateEnum.A_SUCCESSFUL_DEAL.getKey());
        personalTransactionOrderSV.updateById(personalTransactionOrder);

        //-----------------------------------  充值提现变量 start -------------------------------------------------
        String dfAcc = personalTransactionOrder.getDfAcc();            //对方账户
        String ioNum = personalTransactionOrder.getIoNum();            //订单号
        String ioType = personalTransactionOrder.getIoType();        //充值提现订单类型
        // todo 取的是dfAccTypeName
        String dfAccTypeName = personalTransactionOrder.getDfAccType();        //账户类型 (微信 支付宝)
        BigDecimal tdAmount = personalTransactionOrder.getTdAmount();    //充值提现金额
        Long accId = personalTransactionOrder.getAccId();        //用户账户主键
        AccountEntity account = accountSV.selectById(accId);            //用户账户
        Long userId = account.getUserId();                    //用户主键
        UserEntity user = userSV.selectById(userId);                //用户信息
        String userName = user.getRealName() == null ? user.getNickName() : user.getRealName();  //用户姓名
        String title = null;
        String content = null;

        PaymentWayEnum dfAccTypeEnum = DictEnum.valueOfEnum(PaymentWayEnum.class, personalTransactionOrder.getDfAccType());
        TransactionDetailsTdTypeEnum ioTypeEnum = DictEnum.valueOfEnum(TransactionDetailsTdTypeEnum.class, ioType);
        //-----------------------------------  充值提现变量 end -------------------------------------------------

        //3. 更新用户账户
        if (TransactionDetailsTdTypeEnum.RECHARGE.getKey().equals(ioType)) {
            account.setAccBalance(account.getAccBalance().add(tdAmount));
            accountSV.updateById(account);            //更新充值金额
            title = dfAccTypeName + "充值";
            content = dfAccTypeName + "充值";
        } else {
            account.setAccBalance(account.getAccBalance().subtract(tdAmount));
            accountSV.updateById(account);            //更新提现金额
            title = "提现到" + dfAccTypeName;
            content = "提现到" + dfAccTypeName;
        }

        //4.增加用户的交易明细
        transactionDetailsSV.genEmbodyTransactionDetails(accId, otherBillTdNo, dfAcc, dfAccTypeEnum, ioTypeEnum, tdAmount, account.getAccBalance(), ioNum);

        //5. 构建一个支付消息并发送
        informationSV.generatePayInformation(null, userId,
                title, content, null, UserUserTypeEnum.PATIENT.getKey(),
                personalTransactionOrder, personalTransactionOrder.getIoNum(), personalTransactionOrder.getTdAmount(), personalTransactionOrder.getTdTime(), DictEnum.valueOfEnum(PlaformSourceEnum.class, personalTransactionOrder.getPaySource()));


        //6.新增平台交易账单
        if (TransactionDetailsTdTypeEnum.RECHARGE.getKey().equals(ioType)) {
            platformTradeBillSV.insertPlatformTradeBill(userName, user.getPhoneNo(), "充值", "充值", ioNum,
                    BillBusTypeEnum.PAY, dfAcc, tdAmount, personalTransactionOrder.getTdTime(), dfAccTypeEnum, BillStatusEnum.DID_NOT_CHECK, null, otherBillTdNo);
        } else if (TransactionDetailsTdTypeEnum.EMBODY.getKey().equals(ioType)) {
            platformTradeBillSV.insertPlatformTradeBill(userName, user.getPhoneNo(), "提现", "提现", ioNum,
                    BillBusTypeEnum.REFUND, dfAcc, tdAmount, personalTransactionOrder.getTdTime(), dfAccTypeEnum, BillStatusEnum.DID_NOT_CHECK, null, otherBillTdNo);
        }
    }

    /**
     * 送心意订单修改状态
     *
     * @param mindModel
     */
    @Override
    public void updateMindOrder(MindListMobileVO mindModel, String otherBillTdNo) {
        //1. 检查订单
        if (mindModel == null) {
            throw new BusinessException("该订单不存在");
        } else if (PaymentStateEnum.HAVE_TO_PAY.getKey().equals(mindModel.getPayState())) {
            throw new BusinessException("该订单已支付");
        }

        //2.修改订单为已支付
        MindParam mind = new MindParam();
        BeanUtils.copyProperties(mindModel, mind);
        mind.setPayState(PaymentStateEnum.HAVE_TO_PAY.getKey());
        mindSV.updateById(mind.covert());

        //--------------------------------------------------变量 start------------------------------------------------
        Long userId = mindModel.getUserId();        //用户主键
        UserEntity user = userSV.selectById(userId);        //用户信息
        String patientName = user.getRealName() == null ? user.getNickName() : user.getRealName();    //用户姓名
        Long doctorId = mindModel.getDoctorId();   //医生主键
        UserEntity doctorUser = userSV.selectById(mindModel.getDoctorId());
        String doctorName = doctorUser.getRealName() == null ? doctorUser.getNickName() : doctorUser.getRealName();    //医生姓名

        String ioNum = mindModel.getIoNum();                    //订单号
        String payAcc = mindModel.getPayAcc();                    //支付账户
        String paymentWay = mindModel.getPayType();            //支付方式
        BigDecimal thankMoney = mindModel.getThankMoney();        //交易金额
        Date tdTime = mindModel.getTdTime();                    //交易时间
        AccountEntity doctorAccount = accountSV.selectAccountByUserId(doctorId);   //医生账户信息
        AccountEntity userAccount = accountSV.selectAccountByUserId(userId);    //用户账户信息


        BigDecimal discountAmount = BigDecimal.valueOf(0);    //平台垫付金额
        BigDecimal channelAmt = BigDecimal.valueOf(0);  //渠道手续费
        //--------------------------------------------------变量 end------------------------------------------------

        //3.余额支付时更新用户的账户余额
        if (PaymentWayEnum.ACCOUNT.getKey().equals(paymentWay)) {
            if (thankMoney.doubleValue() > userAccount.getAccBalance().doubleValue()) {
                throw new BusinessException("该患者的账户余额不足");
            }
            //减少患者的账户余额
            userAccount.setAccBalance(userAccount.getAccBalance().subtract(mindModel.getThankMoney()));
            accountSV.updateById(userAccount);
        }
        //更新医生的账户余额
        doctorAccount.setAccBalance(doctorAccount.getAccBalance().add(thankMoney));
        accountSV.updateById(doctorAccount);


        //4.增加患者的消费明细
        generateConsumeDetails(userAccount.getId(), CashFlowEnum.EXPEND, ioNum, otherBillTdNo, doctorName + "医生感谢心意-支付", ConsumeDetailsTdTypeEnum.TO_MIND, DictEnum.valueOfEnum(PaymentWayEnum.class, paymentWay),
                payAcc, thankMoney, discountAmount, channelAmt, userAccount.getAccBalance(), tdTime, null, doctorAccount.getId());
        //增加医生的消费明细
        generateConsumeDetails(doctorAccount.getId(), CashFlowEnum.INCOME, ioNum, otherBillTdNo, patientName + "患者感谢心意-收入", ConsumeDetailsTdTypeEnum.TO_MIND, DictEnum.valueOfEnum(PaymentWayEnum.class, paymentWay),
                payAcc, thankMoney, discountAmount, channelAmt, doctorAccount.getAccBalance(), tdTime, null, userAccount.getId());


        //5. 构建一个支付消息并发送
        informationSV.generatePayInformation(doctorId, userId,
                "对" + doctorName + "医生的一点心意", mindModel.getThankContent(), null, UserUserTypeEnum.PATIENT.getKey(),
                mindModel, ioNum, thankMoney, mindModel.getTdTime(), DictEnum.valueOfEnum(PlaformSourceEnum.class, mindModel.getPaySource()));


        //6. 新增平台交易账单
        if (!PaymentWayEnum.ACCOUNT.getKey().equals(mindModel.getPayType())) {
            platformTradeBillSV.insertPlatformTradeBill(patientName, user.getPhoneNo(), "送心意", "送心意订单支付", ioNum,
                    BillBusTypeEnum.PAY, payAcc, thankMoney, tdTime, DictEnum.valueOfEnum(PaymentWayEnum.class, paymentWay), BillStatusEnum.DID_NOT_CHECK, null, otherBillTdNo);
        }
    }

    /**
     * 退费更改问诊订单信息
     *
     * @param order
     * @param otherBillTdNo
     */
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "0#ioNum", noGetMsg = "请稍候！")
    @Override
    public void wxrefundUpdateOrderInfo(InquiryOrderEntity order, String otherBillTdNo) {
        //1. 检查订单
        if (order == null || InquiryOrderPaymentStateEnum.REFUND_OVER.getKey().equals(order.getPaymentState())) {
            LOGGER.error("该订单不存在或该订单已经退过费了");
            return;
        }

        //---------------------------------------------------常量 start----------------------------------------------------------------------------
        //订单信息
        Long patientId = order.getUserId();
        UserEntity user = userSV.selectById(patientId);            //用户信息
        String userName = user.getRealName() == null ? user.getNickName() : user.getRealName();
        String ioNum = order.getIoNum();        //订单号
        Date tdTime = order.getPaymentTime();    //交易时间
        // todo 此处取ioTypeName
        String ioTypeName = order.getIoType();        //订单号
        String payAcc = order.getPayAcc();                //账户
        BigDecimal tdAmount = order.getPaymentPrice();
        String paymentWay = order.getPaymentWay();
        Long couponRecordId = order.getRelId();   //优惠劵领取记录主键

        OrderFlowRecordEntity orderFlowRecord = orderFlowRecordSV.findNewByOrderId(order.getId());    //查询订单中医生最新的订单流转记录
        Long doctorId = orderFlowRecord.getDoctorId();    //抢单医生
        Long doctorDsId = orderFlowRecord.getDoctorDsId();    //医生的工作室id
        BigDecimal paymentPrice = BigDecimal.valueOf(0);  //实际支付金额
        BigDecimal discountAmount = BigDecimal.valueOf(0);    //平台垫付金额
        BigDecimal channelAmt = BigDecimal.valueOf(0);  //渠道手续费
        Long payee = (doctorDsId == null) ? doctorId : doctorStudioSV.selectById(doctorDsId).getBelongPerson();//获取抢单人  （优先获取宿主医生，没有获取抢单医生）
        AccountEntity account = updatePatientFreeze(patientId, tdAmount, "subtract", paymentWay);
        AccountEntity doctorAcc = accountSV.selectAccountByUserId(payee);        //获取抢单医生的基本信息
        UserEntity payeeUser = userSV.selectById(payee);
        String payeeName = payeeUser.getRealName();

        if (couponRecordId != null) {
            //获取平台垫付金额 渠道方手续费
            CouponEntity coupon = couponSV.selectByReciveId(couponRecordId);
            if (coupon != null && CouponChannelEnum.PLATFORM.getKey().equals(coupon.getIsSys())) {
                InquiryOrderParam orderModel = new InquiryOrderParam();
                orderModel.setRelId(order.getRelId());
                orderModel.setPriceId(order.getRuleId());
                Map<String, BigDecimal> payAmt = doctorServiceItemSV.couponUse(orderModel);
                paymentPrice = payAmt.get("paymentPrice");   //实际支付金额
                discountAmount = payAmt.get("discountAmount");    //平台垫付金额
                if (!PaymentWayEnum.ACCOUNT.equals(paymentWay)) {
                    channelAmt = CalcUtil.mul(paymentPrice, 0.006).decimalValue(2);    //渠道手续费
                }
            }
        }

        ConsumeDetailsTdTypeEnum ioTypeEnum = DictEnum.valueOfEnum(ConsumeDetailsTdTypeEnum.class, order.getIoType());
        PaymentWayEnum paymentWayEnum = DictEnum.valueOfEnum(PaymentWayEnum.class, paymentWay);
        //---------------------------------------------------常量 end----------------------------------------------------------------------------

        //2. 新增患者的消费明细
        generateConsumeDetails(account.getId(), CashFlowEnum.INCOME, ioNum, otherBillTdNo, payeeName + "医生的" + ioTypeName + "费-退款", ioTypeEnum,
                paymentWayEnum, payAcc, tdAmount, discountAmount, channelAmt, account.getAccBalance(), tdTime, doctorDsId, doctorAcc.getId());


        //3.退款成功 修改订单状态 订单流转记录状态（已结单）
        orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.THE_CANCELLATION.getKey());
        orderFlowRecordSV.updateById(orderFlowRecord);
        order.setIoState(InquiryOrderStateEnum.CANCEL.getKey());
        order.setPaymentState(InquiryOrderPaymentStateEnum.REFUND_OVER.getKey());
        inquiryOrderDao.updateByPrimaryKeySelective(order);
        //如果使用优惠劵 更改优惠劵为有效状态
        if (couponRecordId != null) {
            CouponReceiveRecordEntity couponReceiveRecord = couponReceiveRecordSV.selectById(couponRecordId);
            couponReceiveRecord.setUserState(CouponUseStateEnum.DONT_USE.getKey());
            couponReceiveRecordSV.updateById(couponReceiveRecord);
        }

        //4. 构建退费消息并发送
        informationSV.generateRefordInformation(order, payee);

        //5. 如果有工作室 将订单移除抢单池
        if (doctorDsId != null) {
            orderPoolSV.remove(RedisKeyEnum.SPECIALIST_DOCTOR_POOL, order.getId().toString(), "WORK", doctorDsId.toString());
        }

        //6. 新增平台交易账单
        if (!PaymentWayEnum.ACCOUNT.getKey().equals(order.getPaymentWay())) {
            //用户姓名,用户手机号,产品名称,交易明细内容,商户订单号,支付流水号,业务类型(01:支付;02:退费;),付款账户,交易金额,交易时间,支付方式,对账状态(01:未对账;02:已对账),创建人,创建时间，修改人,修改时间
            // todo 此处取ioTypeName
            platformTradeBillSV.insertPlatformTradeBill(userName, user.getPhoneNo(), order.getIoType(), "问诊订单退费", order.getIoNum(),
                    BillBusTypeEnum.REFUND, order.getPayAcc(), tdAmount, order.getPaymentTime(), DictEnum.valueOfEnum(PaymentWayEnum.class, order.getPaymentWay()), BillStatusEnum.DID_NOT_CHECK, null, otherBillTdNo);
        }

    }

    /**
     * 支付更改订单信息
     *
     * @param order
     * @param otherBillTdNo 渠道交易订单号
     */
    @Override
    @Locker(key = RedisKeyEnum.REPLY_REFUND_KEY, paramExp = "0#ioNum", noGetMsg = "请稍后!")
    public void updateOrderInfo(InquiryOrderEntity order, String otherBillTdNo) throws Exception {
        if (order == null) {
            throw new BusinessException("该订单不存在");
        } else if (order.getPaymentState().equals(InquiryOrderPaymentStateEnum.HAVE_TO_PAY.getKey())) {
            throw new BusinessException("该订单已经支付过了,请勿重复支付");
        }

        //---------------------------------------------------常量 start----------------------------------------------------------------------------
        //订单信息
        Long patientId = order.getPatientId();
        Long userId = order.getUserId();
        UserEntity user = userSV.selectById(userId);            //用户信息
        String userName = user.getRealName() == null ? user.getNickName() : user.getRealName();
        String ioNum = order.getIoNum();        //订单号
        Date tdTime = order.getPaymentTime();    //交易时间
        //todo 此处取ioTypeName
        String ioTypeName = order.getIoType();        //订单号
        String payAcc = order.getPayAcc();                //账户
        BigDecimal tdAmount = order.getPaymentPrice();
        String paymentWay = order.getPaymentWay();
        Long couponRecordId = order.getRelId();   //优惠劵领取记录主键

        OrderFlowRecordEntity orderFlowRecord = orderFlowRecordSV.findNewByOrderId(order.getId());    //查询订单中医生最新的订单流转记录
        Long doctorId = orderFlowRecord.getDoctorId();    //抢单医生
        Long doctorDsId = orderFlowRecord.getDoctorDsId();    //医生的工作室id
        BigDecimal paymentPrice = BigDecimal.valueOf(0);  //实际支付金额
        BigDecimal discountAmount = BigDecimal.valueOf(0);    //平台垫付金额
        BigDecimal channelAmt = BigDecimal.valueOf(0);  //渠道手续费
        Long payee = (doctorDsId == null) ? doctorId : doctorStudioSV.selectById(doctorDsId).getBelongPerson();//获取抢单人  （优先获取宿主医生，没有获取抢单医生）
        AccountEntity account = updatePatientFreeze(userId, tdAmount, "add", paymentWay);
        AccountEntity doctorAcc = accountSV.selectAccountByUserId(payee);        //获取抢单医生的基本信息
        UserEntity payeeUser = userSV.selectById(payee);
        String payeeName = payeeUser.getRealName();

        if (couponRecordId != null) {
            //获取平台垫付金额 渠道方手续费
            CouponEntity coupon = couponSV.selectByReciveId(couponRecordId);
            if (coupon != null && CouponChannelEnum.PLATFORM.getKey().equals(coupon.getIsSys())) {
                InquiryOrderParam orderModel = new InquiryOrderParam();
                orderModel.setRelId(order.getRelId());
                orderModel.setPriceId(order.getRuleId());
                Map<String, BigDecimal> payAmt = doctorServiceItemSV.couponUse(orderModel);
                paymentPrice = payAmt.get("paymentPrice");   //实际支付金额
                discountAmount = payAmt.get("discountAmount");    //平台垫付金额
                if (!PaymentWayEnum.ACCOUNT.equals(paymentWay)) {
                    channelAmt = CalcUtil.mul(paymentPrice, 0.006).decimalValue(2);    //渠道手续费
                }
            }
        }

        ConsumeDetailsTdTypeEnum ioTypeEnum = DictEnum.valueOfEnum(ConsumeDetailsTdTypeEnum.class, order.getIoType());
        PaymentWayEnum paymentWayEnum = DictEnum.valueOfEnum(PaymentWayEnum.class, paymentWay);
        //---------------------------------------------------常量 end----------------------------------------------------------------------------

        //2. 新增患者的消费明细
        generateConsumeDetails(account.getId(), CashFlowEnum.EXPEND, ioNum, otherBillTdNo, payeeName + "医生的" + ioTypeName + "费-支付", ioTypeEnum,
                paymentWayEnum, payAcc, tdAmount, discountAmount, channelAmt, account.getAccBalance(), tdTime, doctorDsId, doctorAcc.getId());


        // 3.聊天室创建,目前只有电话、视频创建
        createChatRoom(order, orderFlowRecord, doctorDsId);

        //4.支付成功 修改订单状态 判断订单是否需要加入抢单池
        payOverUpdateOrderInfo(order, orderFlowRecord);

        //5. 构建一个支付消息并发送
        // todo 此处取ioTypeName
        informationSV.generatePayInformation(orderFlowRecord.getDoctorId(), order.getUserId(),
                payeeUser.getRealName() + "医生" + order.getIoType() + "费", order.getIllnessDesc(), null, UserUserTypeEnum.PATIENT.getKey(),
                order, order.getIoNum(), order.getPaymentPrice(), order.getPaymentTime(), DictEnum.valueOfEnum(PlaformSourceEnum.class, order.getPaySource()));
        //6. 新增平台交易账单
        // todo 此处取ioTypeName
        if (!PaymentWayEnum.ACCOUNT.getKey().equals(order.getPaymentWay())) {
            platformTradeBillSV.insertPlatformTradeBill(userName, user.getPhoneNo(), order.getIoType(), "问诊订单支付", order.getIoNum(),
                    BillBusTypeEnum.PAY, order.getPayAcc(), tdAmount, order.getPaymentTime(), DictEnum.valueOfEnum(PaymentWayEnum.class, order.getPaymentWay()), BillStatusEnum.DID_NOT_CHECK, null, otherBillTdNo);
        }

	/*	if (!PaymentWayEnum.ACCOUNT.getKey().equals(order.getPaymentWay())) {
			//6.更新平台账户,增加平台交易明细
			platformTradeDetailSV.insertPlaformTradeDetail(ComputeWayEnum.INCREASE.getKey(), tdAmount, consumeId, "问诊订单支付",
					TransactionDetailsTdTypeEnum.PAY.getKey(), CashFlowEnum.INCOME.getKey());
		}*/

        //7.发送支付成功的短信
        if (InquiryOrderIoTypeEnum.BY_CONSULTING.getKey().equals(order.getIoType())) {
            SmsUtil.sendSms(user.getPhoneNo(), SmsTemplateEnum.SUCCESSFUL_PAYMENT_FOR_CONSULTATION, CalcUtil.init(order.getPaymentPrice()).stringValue(true), payeeUser.getRealName());
        } else if (InquiryOrderIoTypeEnum.TELEPHONE_COUNSELING.getKey().equals(order.getIoType())) {
            //查询患者的信息
            PatientDetailEntity patientDetail = patientDetailSV.selectById(patientId);

            //查询通话时长
            Long ruleId = order.getRuleId();
            PhoneRuleEntity phoneRule = phoneRuleSV.selectById(ruleId);
            int time = 0;
            if (phoneRule != null) {
                time = phoneRule.getPrDuration();
            }

            //患者短信
            // todo此处取ioTypeName
            SmsUtil.sendSms(user.getPhoneNo(), SmsTemplateEnum.SUCCESSFUL_TELEPHONE_RESERVATION, payeeUser.getRealName(), order.getIoType(), DateUtil.date(order.getAppointTime()).toString(), String.valueOf(time), "171********");

            //医生短信
            SmsUtil.sendSms(order.getPhoneNo(), SmsTemplateEnum.DOCTOR_S_TELEPHONE_CONSULTATION_IS_ABOUT_TO_START,
                    payeeUser.getRealName(), patientDetail.getName());
        }

        // 8. 48小时医生未接单自动退费 (仅限图文问诊)
        if (InquiryOrderIoTypeEnum.BY_CONSULTING.getKey().equals(order.getIoType())) {
            JSONObject reqParams = new JSONObject();
            reqParams.put("ioNum", order.getIoNum());
            reqParams.put("id", order.getId());
            AutoRefundOrderTask autoRefundOrderTask = new AutoRefundOrderTask("48小时医生未接单自动退费", TimeUnit.HOURS.toMillis(orderRefundTime()), reqParams);
            messageSenderSV.sendDelayTask(autoRefundOrderTask);
        }
    }

    /**
     * 获取医生自动退费时间
     *
     * @return
     */
    private long orderRefundTime() {
        Long orderCompletionTime = 1L;
        Long valueByKey = sysConfigSV.getValueByKey(Constants.SysConfigKey.ORDER_AUTOREFUND_TIME, Long::new);
        if (valueByKey != null) {
            return valueByKey;
        }
        return orderCompletionTime;
    }


    /**
     * 咨询室创建
     *
     * @param inquiryOrderModel 订单信息
     * @param orderFlowRecord   流转信息
     * @param doctorStudioId    工作室ID
     */
    private void createChatRoom(InquiryOrderEntity inquiryOrderModel, OrderFlowRecordEntity orderFlowRecord, Long doctorStudioId) throws Exception {
        String ioType = inquiryOrderModel.getIoType();
        if (InquiryOrderIoTypeEnum.BY_CONSULTING.getKey().equals(ioType)
                || InquiryOrderIoTypeEnum.PRVIVATE_DOCTOR.getKey().equals(ioType)) {
            return;
        }

        // 电话问诊和视频问诊需要提前创建聊天室
        String roomId = IdUtil.fastSimpleUUID();
        //查询医生和患者云信账号
        List<YunxinUserEntity> users = iYunxinUserSV.selectYunXinId(orderFlowRecord.getDoctorId(), inquiryOrderModel.getUserId());
        //医生在工作室人员表中的id
        StudioPersonnelEntity model2 = null;
        StudioPersonnelEntity studioPersonnel = null;
        CounsellingRoomEntity room = new CounsellingRoomEntity();
        if (doctorStudioId != null) {
            model2 = StudioPersonnelEntity.builder().doctId(orderFlowRecord.getDoctorId()).dsId(doctorStudioId).build();
            studioPersonnel = iStudioPersonnelSV.selectOne(model2);
            room.setDsId(studioPersonnel.getId());
        }
        //订单id/工作室人员主键/流转主键/问诊人云信id
        room.setIoId(inquiryOrderModel.getId());
        room.setOrderFlowRecordId(orderFlowRecord.getId());
        room.setInquiryAccid(users.get(1).getAccid());
        //聊天室id/回复人云信id/
        room.setRoomId(roomId);
        room.setReplyAccid(users.get(0).getAccid());
        //咨询状态 未回复/咨询室类型() 专科
        room.setCourState(CounsellingRoomStateEnum.DID_NOT_RETURN.getKey());
        room.setCourType(CounsellingRoomCourTypeEnum.SPECIALIZED_SUBJECT.getKey());
        room.setCreateTime(new Date());
        iCounsellingRoomSV.insert(room);
        //添加双方聊天人员
        String doctorToken = null;
        String userToken = null;
        // todo 视频咨询跳过
		/*if (InquiryOrderIoTypeEnum.VIDEO_CONSULTATION.getKey().equals(ioType)) {
			Map<String, String> roomToken = qiNiuRoomManagerSV.createRoomToken(inquiryOrderModel.getId(), room.getRoomId(), true);
			Set<String> keys = roomToken.keySet();
			for (String key : keys) {
				if (key.contains("admin")) {
					doctorToken = roomToken.get(key);
				} else if (key.contains("user")) {
					userToken = roomToken.get(key);
				}
			}
		}*/
        ChatroomPersonnelEntity chatroomPersonnel = new ChatroomPersonnelEntity();
        chatroomPersonnel.setCourId(room.getId());
        chatroomPersonnel.setPersonType(UserUserTypeEnum.PATIENT.getKey());
        chatroomPersonnel.setCrpUserid(inquiryOrderModel.getUserId());
        chatroomPersonnel.setRoomToken(userToken);
        chatroomPersonnel.setCreateTime(new Date());
        chatroomPersonnelSV.insert(chatroomPersonnel);
        chatroomPersonnel.setCrpUserid(orderFlowRecord.getDoctorId());
        chatroomPersonnel.setRoomToken(doctorToken);
        chatroomPersonnel.setPersonType(UserUserTypeEnum.DOCTOR.getKey());
        chatroomPersonnelSV.insert(chatroomPersonnel);
    }

    //------------------------------------------------------------ start 订单支付回调接口方法分支-------------------------------------------------------------------------

    /**
     * 更新患者的冻结金额  (仅限余额支付)
     *
     * @param userId     用户id
     * @param tdAmount   更新金额
     * @param upType     更新方式(加减)
     * @param paymentWay 支付方式
     */
    public AccountEntity updatePatientFreeze(Long userId, BigDecimal tdAmount, String upType, String paymentWay) {
        AccountEntity patientAccount = accountSV.selectAccountByUserId(userId);
        if (PaymentWayEnum.ACCOUNT.getKey().equals(paymentWay)) {
            //余额支付才会更新
            if ("add".equals(upType)) {
                patientAccount.setAccBalance(patientAccount.getAccBalance().subtract(tdAmount));
                //增加患者的冻结金额
                patientAccount.setFreezeBalance(patientAccount.getFreezeBalance().add(tdAmount));
            } else {
                patientAccount.setAccBalance(patientAccount.getAccBalance().add(tdAmount));
                //减少患者的冻结金额
                patientAccount.setFreezeBalance(patientAccount.getFreezeBalance().subtract(tdAmount));
            }
            accountSV.updateAccountById(patientAccount);
        }
        return patientAccount;
    }

    /**
     * 新增患者的交易明细
     *
     * @param accId          账户主键
     * @param cashFlow       收入支出(CashFlowEnum)
     * @param ioNum          订单号
     * @param transactionId  微信订单号
     * @param tdBody         交易内容
     * @param tdTypeEnum     交易类型(ConsumeDetailsTdTypeEnum)
     * @param paymentWayEnum 支付方式(PaymentWayEnum)
     * @param payAcc         支付账户
     * @param tdAmount       交易金额
     * @param advanceAmount  平台垫付金额
     * @param otherAmt       渠道手续费
     * @param aftTdAmount    交易后账户余额
     * @param tdTime         交易时间
     * @param studioId       工作室id
     * @param dfAccId        对方账户主键
     * @return
     */
    private Long generateConsumeDetails(Long accId, CashFlowEnum cashFlow, String ioNum, String transactionId, String tdBody, ConsumeDetailsTdTypeEnum tdTypeEnum,
                                        PaymentWayEnum paymentWayEnum, String payAcc, BigDecimal tdAmount, BigDecimal advanceAmount, BigDecimal otherAmt,
                                        BigDecimal aftTdAmount, Date tdTime, Long studioId, Long dfAccId) {
        ConsumeDetailsEntity consumeDetails = new ConsumeDetailsEntity();
        consumeDetails.setAccId(accId);
        //  交易单号
        consumeDetails.setTdNo(transactionId);
        consumeDetails.setTradeNo(ioNum);
        consumeDetails.setTdBody(tdBody);
        consumeDetails.setTdType(tdTypeEnum.getKey());
        consumeDetails.setCashFlow(cashFlow.getKey());
        consumeDetails.setPayType(paymentWayEnum.getKey());
        consumeDetails.setPayAcc(payAcc);
        consumeDetails.setTdAmount(tdAmount);
        consumeDetails.setAdvanceAmount(advanceAmount);
        consumeDetails.setOtherAmt(otherAmt);
        consumeDetails.setAftTdAmount(aftTdAmount);
        consumeDetails.setTdTime(tdTime);
        consumeDetails.setStudioId(studioId);
        consumeDetails.setDfAccId(dfAccId);
        consumeDetails.setCreateTime(new Date());
        consumeDetailsSV.insert(consumeDetails);
        return consumeDetails.getId();

		/*if (ConsumeDetailsTdTypeEnum.TO_MIND.getKey().equals(tdTypeEnum.getKey())) {
			//送心意
			MindModel mind = (MindModel) object;
			consumeDetails.setTradeNo(mind.getIoNum());
			if (CashFlowEnum.EXPEND.getKey().equals(cashFlow)) {
				consumeDetails.setTdBody(dfName + "医生感谢心意-支付");

			} else {
				consumeDetails.setTdBody(dfName + "患者感谢心意-收入");
			}
			consumeDetails.setTdAmount(mind.getThankMoney());
			consumeDetails.setPayAcc(mind.getPayAcc());
			consumeDetails.setTdTime(mind.getTdTime());
		} else {
			//问诊订单
			InquiryOrder order = (InquiryOrder) object;
			//  订单号
			consumeDetails.setTradeNo(order.getIoNum());
			String textWay = ConsumeDetailsTdTypeEnum.BY_CONSULTING.getKey();
			String phoneWay = ConsumeDetailsTdTypeEnum.TELEPHONE_COUNSELING.getKey();
			//交易明细内容
			if (CashFlowEnum.EXPEND.getKey().equals(cashFlow)) {
				//患者交易明细
				if (textWay.equals(order.getIoType())) {
					consumeDetails.setTdBody(dfName + "医生的图文问诊费-支付");
				} else if (phoneWay.equals(order.getIoType())) {
					consumeDetails.setTdBody(dfName + "医生的电话问诊费-支付");
				}
			} else {
				if (textWay.equals(order.getIoType())) {
					consumeDetails.setTdBody(dfName + "医生的图文问诊费-退款");
				} else if (phoneWay.equals(order.getIoType())) {
					consumeDetails.setTdBody(dfName + "医生的电话问诊费-退款");
				}
			}
			consumeDetails.setTdAmount(order.getPaymentPrice());
			consumeDetails.setPayAcc(order.getPayAcc());
			consumeDetails.setTdTime(order.getPaymentTime());
		}*/


    }

    /**
     * 支付成功 修改订单状态 判断订单是否需要加入抢单池
     *
     * @param order
     * @param orderFlowRecord
     */
    public void payOverUpdateOrderInfo(InquiryOrderEntity order, OrderFlowRecordEntity orderFlowRecord) {
        InquiryOrderIoTypeEnum enumByCode = DictEnum.valueOfEnum(InquiryOrderIoTypeEnum.class, order.getIoType());
        switch (enumByCode) {
            case BY_CONSULTING:
                //1.图文问诊
                if (orderFlowRecord.getDoctorDsId() == null) {
                    //1.1表示医生没有工作室，直接将订单给医生
                    order.setIoState(InquiryOrderStateEnum.HAVE_ORDER.getKey());
                    orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.HAS_SEIZED_THE_SINGLE.getKey());
                } else {
                    // 1.2表示医生有工作室
                    if (orderFlowRecord.getDoctorId() == null) {
                        //1.2.1 表示将订单直接给该工作室
                        orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.NOT_ROB_SINGLE.getKey());
                        order.setIoState(InquiryOrderStateEnum.WAITING_LIST.getKey());
                        orderPoolSV.put(RedisKeyEnum.SPECIALIST_DOCTOR_POOL, order.getId().toString(), "WORK", orderFlowRecord.getDoctorDsId().toString());
                    } else {
                        //1.2.2 表示将订单直接给该工作室的该医生
                        order.setIoState(InquiryOrderStateEnum.HAVE_ORDER.getKey());
                        orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.HAS_SEIZED_THE_SINGLE.getKey());
                    }
                }
                break;
            case TELEPHONE_COUNSELING:
                //2.电话问诊
                order.setIoState(InquiryOrderStateEnum.HAVE_ORDER.getKey());
                orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.HAS_SEIZED_THE_SINGLE.getKey());
                break;
            case VIDEO_CONSULTATION:
                //3.视频问诊
                order.setIoState(InquiryOrderStateEnum.HAVE_ORDER.getKey());
                orderFlowRecord.setStatus(OrderFlowRecordSatusEnum.HAS_SEIZED_THE_SINGLE.getKey());
                break;
            case PRVIVATE_DOCTOR:
                //4.私人医生
                break;
        }
        //支付状态
        order.setPaymentState(InquiryOrderPaymentStateEnum.HAVE_TO_PAY.getKey());
        //更改订单信息
        inquiryOrderDao.updateByPrimaryKeySelective(order);
        //更改订单流转状态
        orderFlowRecordSV.updateById(orderFlowRecord);
        //来新单了
        jpushAccountSV.pushOrderMsg2Doctors(order.getId());
    }
    //------------------------------------------------------------ end 订单支付回调接口方法分支-------------------------------------------------------------------------

}
